home *** CD-ROM | disk | FTP | other *** search
- /* +-------------------------------------------------------------------+ */
- /* | Copyright 1992, 1993, David Koblas (koblas@netcom.com) | */
- /* | | */
- /* | Permission to use, copy, modify, and to distribute this software | */
- /* | and its documentation for any purpose is hereby granted without | */
- /* | fee, provided that the above copyright notice appear in all | */
- /* | copies and that both that copyright notice and this permission | */
- /* | notice appear in supporting documentation. There is no | */
- /* | representations about the suitability of this software for | */
- /* | any purpose. this software is provided "as is" without express | */
- /* | or implied warranty. | */
- /* | | */
- /* +-------------------------------------------------------------------+ */
-
- #include <X11/Intrinsic.h>
- #include <X11/StringDefs.h>
- #include <X11/Shell.h>
- #include <X11/Xatom.h>
- #include <stdio.h>
- #include "palette.h"
- #include "hash.h"
- #include "misc.h"
- #include "image.h"
- #include "xpaint.h"
-
- typedef struct list_s {
- Display *dpy;
- Colormap cmap;
- Palette *map;
- struct list_s *next;
- } PaletteList;
-
- typedef struct Col_s {
- XColor color;
- Boolean used;
- Boolean invalid;
- } Col;
-
- #define HASH_SIZE 128
- #define HASH(c) ((int)((c).red + (c).green + (c).blue) % HASH_SIZE)
- #define HASH_PIXEL(c) (c % HASH_SIZE)
-
- static PaletteList *cmapList = NULL;
-
- static int readCMP(Col *cca, Col *ccb)
- {
- XColor *ca = &cca->color, *cb = &ccb->color;
-
- if (ca->red != cb->red)
- return ca->red < cb->red ? -1 : 1;
- if (ca->green != cb->green)
- return ca->green < cb->green ? -1 : 1;
- if (ca->blue != cb->blue)
- return ca->blue < cb->blue ? -1 : 1;
- return 0;
- }
-
- static int cmpPixel(Col *cca, Col *ccb)
- {
- if (cca->color.pixel == ccb->color.pixel)
- return 0;
- return (cca->color.pixel < ccb->color.pixel) ? -1 : 1;
- }
-
- static void freeFunc(void *junk)
- {
- /*
- ** Nop free func
- */
- }
-
- static void entryUnlink(Palette *map, Col *node)
- {
- node->used = True;
- node->invalid = False;
- map->nfree--;
- }
-
-
- #define STEP 256
-
- static Palette *paletteNew(Widget w, Boolean useDefault)
- {
- int i, v;
- Display *dpy = XtDisplay(w);
- Screen *screen = XtScreen(w);
- Colormap rcmap = DefaultColormapOfScreen(screen);
- Palette *map = XtNew(Palette);
- Col *ctable;
- Visual *visual = NULL;
- PaletteList *new;
- int end, depth = -1;
- Boolean flg = False;
-
- XtVaGetValues(w, XtNvisual, &visual, XtNdepth, &depth, NULL);
- if (visual == NULL || useDefault)
- visual = DefaultVisualOfScreen(screen);
- if (depth <= 0 || useDefault)
- depth = DefaultDepthOfScreen(screen);
-
- map->htable = NULL;
- map->ltable = NULL;
- map->display = dpy;
- map->htable = HashCreate(readCMP, freeFunc, HASH_SIZE);
- map->ltable = HashCreate(cmpPixel, freeFunc, HASH_SIZE);
- map->list = NULL;
- map->last = NULL;
- map->isMapped= visual->class != TrueColor;
- map->isGrey = (visual->class == StaticGray || visual->class == GrayScale);
- map->ncolors = visual->map_entries;
- map->nfree = 0;
- map->ctable = NULL;
- map->visual = visual;
- map->depth = depth;
- map->userList = NULL;
- map->isDefault = False;
- map->mine = None;
-
- switch (visual->class) {
- case TrueColor:
- map->rShift = 0;
- map->gShift = 0;
- map->bShift = 0;
- map->rRange = 1;
- map->gRange = 1;
- map->bRange = 1;
- for (v = visual->red_mask; (v & 1) == 0; v >>= 1)
- map->rShift++;
- for (; (v & 1) == 1; v >>= 1)
- map->rRange <<= 1;
- for (v = visual->green_mask; (v & 1) == 0; v >>= 1)
- map->gShift++;
- for (; (v & 1) == 1; v >>= 1)
- map->gRange <<= 1;
- for (v = visual->blue_mask; (v & 1) == 0; v >>= 1)
- map->bShift++;
- for (; (v & 1) == 1; v >>= 1)
- map->bRange <<= 1;
- case StaticGray:
- case StaticColor:
- map->readonly = True;
- map->cmap = XCreateColormap(dpy, RootWindowOfScreen(screen),
- visual, AllocNone);
- map->isDefault = False;
- goto addlist;
- default:
- map->readonly = False;
- if (useDefault) {
- map->cmap = rcmap;
- map->isDefault = True;
- } else {
- map->cmap = XCreateColormap(dpy, RootWindowOfScreen(screen),
- visual, AllocAll);
- }
- break;
- }
-
- ctable = (Col *)XtCalloc(sizeof(Col), visual->map_entries);
- map->ctable = ctable;
- end = CellsOfScreen(screen);
- for (i = 0; i < visual->map_entries; i += STEP) {
- XColor xcol[STEP];
- int cnt = visual->map_entries - i;
- int j, d;
-
- if (cnt > STEP)
- cnt = STEP;
-
- for (j = 0; j < cnt; j++) {
- Col *c = &ctable[i + j];
-
- c->color.pixel = i + j;
- c->color.flags = DoRed|DoGreen|DoBlue;
- xcol[j].pixel = i + j;
- xcol[j].flags = DoRed|DoGreen|DoBlue;
-
- c->used = False;
- c->invalid = False;
- }
-
- if (i >= end)
- d = 0;
- else
- d = MIN(end - i, cnt);
-
- if (i < end)
- XQueryColors(dpy, rcmap, xcol, d);
- if (!flg) {
- for (j = d; j < cnt; j++) {
- xcol[j].flags = DoRed|DoGreen|DoBlue;
- xcol[j].red = xcol[j].green = xcol[j].blue = 0xffff;
- }
- if (d == 0)
- flg = True;
- }
-
- if (!map->isDefault)
- XStoreColors(dpy, map->cmap, xcol, cnt);
-
- for (j = 0; j < cnt; j++) {
- Col *c = &ctable[i + j];
-
- c->color.red = xcol[j].red & 0xff00;
- c->color.green = xcol[j].green & 0xff00;
- c->color.blue = xcol[j].blue & 0xff00;
- HashAdd(map->htable, HASH(c->color), c);
- HashAdd(map->ltable, HASH_PIXEL(c->color.pixel), c);
- }
- }
-
- map->nfree = visual->map_entries;
-
- if (!map->isDefault) {
- Boolean got = False;
-
- got = !map->isMapped;
- for (i = 0; i < visual->map_entries; i++) {
- if (ctable[i].color.pixel == BlackPixelOfScreen(screen)) {
- entryUnlink(map, &ctable[i]);
- } else if (ctable[i].color.pixel == WhitePixelOfScreen(screen)) {
- entryUnlink(map, &ctable[i]);
- } else if (!got) {
- map->mine = i;
- entryUnlink(map, &ctable[i]);
- got = True;
- }
- }
- }
-
- addlist:
- new = (PaletteList*)XtMalloc(sizeof(PaletteList));
- new->dpy = XtDisplay(w);
- new->cmap = map->cmap;
- new->next = cmapList;
- new->map = map;
- cmapList = new;
-
- return map;
- }
-
- Palette *PaletteCreate(Widget w)
- {
- return paletteNew(GetShell(w), False);
- }
-
- Palette *PaletteGetDefault(Widget w)
- {
- static Palette *defMap = NULL;
-
- if (defMap == NULL)
- defMap = paletteNew(GetShell(w), True);
-
- return defMap;
- }
- static Palette *paletteGetBW()
- {
- static Palette *map = NULL;
- XColor xcol;
- Col *ctable;
-
- if (map != NULL)
- return map;
-
- map = XtNew(Palette);
-
- map->display = NULL;
- map->htable = HashCreate(readCMP, freeFunc, HASH_SIZE);
- map->ltable = HashCreate(cmpPixel, freeFunc, HASH_SIZE);
- map->list = NULL;
- map->last = NULL;
- map->isMapped= True;
- map->isGrey = True;
- map->ncolors = 2;
- map->nfree = 0;
- map->ctable = NULL;
- map->visual = None; /* X */
- map->depth = 1;
- map->userList= NULL;
- map->isDefault= False;
- map->mine = None;
- map->cmap = None;
-
- ctable = (Col *)XtCalloc(sizeof(Col), map->ncolors);
- map->ctable = ctable;
-
- ctable[0].color.pixel = 0;
- ctable[0].color.flags = DoRed|DoGreen|DoBlue;
- ctable[0].color.red = 0xffff;
- ctable[0].color.green = 0xffff;
- ctable[0].color.blue = 0xffff;
- ctable[0].used = True;
- ctable[0].invalid = False;
-
- ctable[1].color.pixel = 1;
- ctable[1].color.flags = DoRed|DoGreen|DoBlue;
- ctable[1].color.red = 0x0000;
- ctable[1].color.green = 0x0000;
- ctable[1].color.blue = 0x0000;
- ctable[1].used = True;
- ctable[1].invalid = False;
-
- HashAdd(map->htable, HASH(ctable[0].color), &ctable[0]);
- HashAdd(map->htable, HASH(ctable[1].color), &ctable[1]);
- HashAdd(map->ltable, HASH_PIXEL(ctable[0].color.pixel), &ctable[0]);
- HashAdd(map->ltable, HASH_PIXEL(ctable[1].color.pixel), &ctable[1]);
-
- return map;
- }
-
- static void addColor(Palette *map, XColor *color)
- {
- Col *node, *n;
- int i;
-
- if (map->readonly) {
- /*
- ** The temporary color is needed since alloc
- ** will change the pixel values.
- */
- XColor newc;
- newc = *color;
- node = XtNew(Col);
-
- newc.flags = DoRed|DoGreen|DoBlue;
- XAllocColor(map->display, map->cmap, &newc);
- node->color.pixel = color->pixel = newc.pixel;
- } else {
- Col *cptr = (Col*)map->ctable;
- int d, curDif = -1;
- int rd, gd, bd;
-
- /*
- ** Find the closest match in the existing color map.
- */
- node = cptr;
-
- for (i = 0; i < map->ncolors; i++, cptr++) {
- if (map->nfree != 0 && cptr->used)
- continue;
-
- rd = (cptr->color.red >> 8) - (color->red >> 8);
- gd = (cptr->color.green >> 8) - (color->green >> 8);
- bd = (cptr->color.blue >> 8) - (color->blue >> 8);
- d = rd * rd + gd * gd + bd * bd;
-
- if (d < curDif || curDif == -1) {
- node = cptr;
- curDif = d;
- }
- }
-
- /*
- ** All free colors used up.
- */
- if (map->nfree == 0) {
- color->pixel = node->color.pixel;
- return;
- }
-
- /*
- ** Get next free color.
- */
- entryUnlink(map, node);
-
- n = HashFind(map->ltable, HASH_PIXEL(node->color.pixel), node);
- HashRemove(map->ltable, HASH_PIXEL(color->pixel), n);
- HashRemove(map->htable, HASH(*color), node);
-
- color->pixel = node->color.pixel;
- }
-
- node->used = True;
- node->invalid = False;
- node->color.red = color->red & 0xff00;
- node->color.green = color->green & 0xff00;
- node->color.blue = color->blue & 0xff00;
- node->color.flags = DoRed|DoGreen|DoBlue;
- if (!map->readonly)
- XStoreColor(map->display, map->cmap, &node->color);
- HashAdd(map->htable, HASH(*color), node);
- HashAdd(map->ltable, HASH_PIXEL(color->pixel), node);
- }
-
- static Col *allocN(Palette *map, int ncolor, Boolean *flags, XColor *colors, Pixel *list)
- {
- Col c, *node = NULL;
- int i;
-
- for (i = 0; i < ncolor; i++) {
- if (flags != NULL && flags[i])
- continue;
-
- c.color.red = colors[i].red & 0xff00;
- c.color.green = colors[i].green & 0xff00;
- c.color.blue = colors[i].blue & 0xff00;
-
- if ((node = HashFind(map->htable, HASH(c.color), &c)) == NULL) {
- addColor(map, &colors[i]);
- list[i] = colors[i].pixel;
- } else {
- /*
- ** It must have been allocated in the previous
- ** pass, or by the above.
- */
- if (!node->used)
- entryUnlink(map, node);
- list[i] = node->color.pixel;
- }
- }
-
- return node;
- }
-
- int PaletteAllocN(Palette *map, XColor *color, int ncolor, Pixel *list)
- {
- Boolean *flg = XtCalloc(sizeof(Boolean), ncolor);
- Col c, *node;
- int i;
- Boolean newMine = False;
-
- if (!map->isMapped) {
- for (i = 0; i < ncolor; i++) {
- unsigned int r, g, b;
-
- r = (color[i].red * map->rRange) >> 16;
- g = (color[i].green * map->gRange) >> 16;
- b = (color[i].blue * map->bRange) >> 16;
- list[i] = (r << map->rShift)|(g << map->gShift)|(b << map->bShift);
- }
- return 0;
- }
-
- for (i = 0; i < ncolor; i++) {
- Col c;
-
- c.color.red = color[i].red & 0xff00;
- c.color.green = color[i].green & 0xff00;
- c.color.blue = color[i].blue & 0xff00;
-
- if ((node = HashFind(map->htable, HASH(c.color), &c)) != NULL) {
- flg[i] = True;
- /*
- ** Match found, if the entry hasn't been "alloced"
- ** yet, mark it so, and remove it from the "free" list.
- */
- if (!node->used)
- entryUnlink(map, node);
- list[i] = node->color.pixel;
- if (list[i] == map->mine)
- newMine = True;
- } else {
- flg[i] = False;
- }
- }
-
- for (i = 0; i < ncolor; i++) {
- if (flg[i])
- continue;
-
- c.color.red = color[i].red & 0xff00;
- c.color.green = color[i].green & 0xff00;
- c.color.blue = color[i].blue & 0xff00;
-
- if ((node = HashFind(map->htable, HASH(c.color), &c)) == NULL) {
- addColor(map, &color[i]);
- list[i] = color[i].pixel;
- } else {
- /*
- ** It must have been allocated in the previous
- ** pass, or by the above.
- */
- list[i] = node->color.pixel;
- }
- if (list[i] == map->mine)
- newMine = True;
- }
-
- XtFree((XtPointer)flg);
-
- if (newMine && map->ctable != NULL) {
- Col *cptr = (Col*)map->ctable;
-
- for (i = 0; i < map->ncolors; i++, cptr++) {
- if (cptr->used)
- continue;
- map->mine = cptr->color.pixel;
- break;
- }
- }
-
- return 0;
- }
-
- Pixel PaletteAlloc(Palette *map, XColor *color)
- {
- if (!map->isMapped) {
- unsigned int r, g, b;
-
- r = (color->red * map->rRange) >> 16;
- g = (color->green * map->gRange) >> 16;
- b = (color->blue * map->bRange) >> 16;
- return (r << map->rShift)|(g << map->gShift)|(b << map->bShift);
- }
-
- if (map->last != NULL) {
- XColor *lc = (XColor*)map->last;
-
- if (lc->red == color->red &&
- lc->green == color->green &&
- lc->blue == color->blue)
- return color->pixel = lc->pixel;
- }
-
- map->last = allocN(map, 1, NULL, color, &color->pixel);
-
- return color->pixel;
- }
-
- /*
- ** Given a Pixel value on the specified map return the
- ** RGV value.
- **
- ** Special case "TrueColor" since it is just computed.
- **
- */
- XColor *PaletteLookup(Palette *map, Pixel pix)
- {
- if (map->isMapped) {
- Col col;
- Col *c;
-
- col.color.pixel = pix;
-
- if ((c = (Col*)HashFind(map->ltable, HASH_PIXEL(pix), &col)) == NULL) {
- printf("Shouldn't happen\n");
- return NULL;
- }
-
- if (c->invalid) {
- HashRemove(map->htable, HASH(c->color), c);
- XQueryColor(map->display, map->cmap, &c->color);
- c->color.red &= 0xff00;
- c->color.green &= 0xff00;
- c->color.blue &= 0xff00;
- HashAdd(map->htable, HASH(c->color), c);
- c->invalid = False;
- }
-
- return &c->color;
- } else {
- static XColor xc;
-
- xc.red = (pix >> map->rShift) & (map->rRange-1);
- xc.green = (pix >> map->gShift) & (map->gRange-1);
- xc.blue = (pix >> map->bShift) & (map->bRange-1);
-
- xc.red *= 65536 / map->rRange;
- xc.green *= 65536 / map->gRange;
- xc.blue *= 65536 / map->bRange;
-
- return &xc;
- }
- }
-
- Boolean PaletteLookupColor(Palette *map, XColor *col, Pixel *pxl)
- {
- Col c, *node;
-
- if (!map->isMapped) {
- unsigned int r, g, b;
-
- r = (col->red * map->rRange) >> 16;
- g = (col->green * map->gRange) >> 16;
- b = (col->blue * map->bRange) >> 16;
- *pxl = (r << map->rShift)|(g << map->gShift)|(b << map->bShift);
- return True;
- }
-
- c.color.red = col->red & 0xff00;
- c.color.green = col->green & 0xff00;
- c.color.blue = col->blue & 0xff00;
-
- if ((node = HashFind(map->htable, HASH(c.color), &c)) != NULL) {
- *pxl = node->color.pixel;
- return True;
- }
-
- return False;
- }
-
- Pixel PaletteGetUnused(Palette *map)
- {
- return map->mine;
- }
-
- /*
- ** Change the RGB value of a pixel
- */
- void PaletteSetInvalid(Palette *map, Pixel pix)
- {
- Col col, *c;
-
- if (!map->isMapped || map->readonly)
- return;
-
- col.color.pixel = pix;
-
- if ((c = (Col*)HashFind(map->ltable, HASH_PIXEL(pix), &col)) == NULL)
- return;
-
- c->invalid = True;
- }
-
- /*
- ** This will fail if on a read only colormap
- */
- Boolean PaletteSetPixel(Palette *map, Pixel pixel, XColor *xcol)
- {
- Col col, *c;
-
- if (map->readonly)
- return False;
-
- if (map->isDefault)
- return False;
-
- xcol->pixel = pixel;
- xcol->flags = DoRed|DoGreen|DoBlue;
- XStoreColor(map->display, map->cmap, xcol);
-
- col.color.pixel = pixel;
-
- if ((c = (Col*)HashFind(map->ltable, HASH_PIXEL(pixel), &col)) != NULL)
- c->invalid = True;
-
- return True;
- }
-
- Palette *PaletteFindDpy(Display *dpy, Colormap cmap)
- {
- PaletteList *cur;
-
- if (cmap == -1)
- return paletteGetBW();
-
- for (cur = cmapList; cur != NULL; cur = cur->next)
- if (cur->cmap == cmap && cur->dpy == dpy)
- return cur->map;
-
- return NULL;
- }
-
- Palette *PaletteFind(Widget w, Colormap cmap)
- {
- return PaletteFindDpy(XtDisplay(w), cmap);
- }
-
- static void paletteAddUserDestroy(Widget w, Palette *map, XtPointer junk)
- {
- paletteUserList *cur = map->userList;
- paletteUserList **prev = &map->userList;
-
- while (cur != NULL && cur->widget != w) {
- prev = &cur->next;
- cur = cur->next;
- }
-
- if (cur == NULL)
- return;
-
- *prev = cur->next;
- XtFree((XtPointer)cur);
- }
-
- void PaletteDelete(Palette *map)
- {
- PaletteList *cur, **prev;
- paletteUserList *ul, *nx;
-
- HashDestroy(map->htable);
- HashDestroy(map->ltable);
- if (map->ctable != NULL)
- XtFree((XtPointer)map->ctable);
-
- for (cur = cmapList, prev = &cmapList; cur != NULL && cur->map != map;
- prev = &cur->next, cur = cur->next);
-
- if (cur != NULL)
- *prev = cur->next;
-
- ul = map->userList;
- while (ul != NULL) {
- nx = ul->next;
- XtRemoveCallback(ul->widget, XtNdestroyCallback, (XtCallbackProc)paletteAddUserDestroy, (XtPointer)map);
- XtFree((XtPointer)ul);
- ul = nx;
- }
-
- XtFree((XtPointer)map);
- XtFree((XtPointer)cur);
- }
-
- void PaletteAddUser(Palette *map, Widget w)
- {
- paletteUserList *n = XtNew(paletteUserList);
-
- n->widget = w;
- n->next = map->userList;
- map->userList = n;
- XtAddCallback(w, XtNdestroyCallback, (XtCallbackProc)paletteAddUserDestroy, (XtPointer)map);
- }
-